#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include "..\global\globaldef.h"
#include "CommServer.h"
#include "PacketConven.h"
#include "..\Global\globaldef.h"

#pragma message ("take out when refractoring exception handler")
#include <stdio.h>
using namespace std;

Server::Server()
{
	m_listenSocket = INVALID_SOCKET;
	m_hListenThread = NULL;
	m_listening = false;
	m_pAddressInfo = NULL;
	//InitializeCriticalSection(&criticalSection);
}

Server::~Server()
{
	Release();
	//DeleteCriticalSection(&criticalSection);
}

bool Server::Initalize()
{
	WORD wVersionRequested;
	WSADATA wsaData;
	struct addrinfo *pAddressInfo = NULL, hints;

	VTRY
	{
		wVersionRequested = MAKEWORD(2, 2);

		if (WSAStartup(wVersionRequested, &wsaData) != 0)
			VTHROW("The winsock library was not found")

		if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2 )
			VTHROW("Incorrect winsock version");
		
		ZeroMemory(&hints, sizeof (hints));
		hints.ai_family = AF_INET;
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_protocol = IPPROTO_TCP;
		hints.ai_flags = AI_PASSIVE;

		char buffer[128] = {'\0'};
		sprintf_s(buffer, 128, "%i", COMM_PORT_I);

		if (getaddrinfo(NULL, &buffer[0], &hints, &pAddressInfo) != 0)
			VTHROW("getaddrinfo failed");
		
		m_pAddressInfo = pAddressInfo;
		return true;
	}
	VCATCHONLY;

	EndListenForClients();
	pAddressInfo = NULL;
	m_clients.clear();
	return false;
}

bool Server::Release()
{
	bool result = true;

	if(m_listening)
		result = result && EndListenForClients();

	//EnterCriticalSection(&criticalSection);
	for(unsigned int i = 0; i < m_clients.size(); i++)	{

		if(m_clients[i]) {
			result = result && m_clients[i]->Release();
			delete m_clients[i];
		}
	}
	m_clients.clear();
	//LeaveCriticalSection(&criticalSection);
	freeaddrinfo(m_pAddressInfo);
	m_pAddressInfo = NULL;
	return result;
}



bool Server::BeginListenForClients()
{
	SOCKET listenSocket = INVALID_SOCKET;
	HANDLE hListenThread = NULL;

	VTRY
	{
		if(!m_pAddressInfo)
			VTHROW("Server not initalized");

		listenSocket = socket(m_pAddressInfo->ai_family, m_pAddressInfo->ai_socktype, m_pAddressInfo->ai_protocol);
		if (listenSocket == INVALID_SOCKET)
			VTHROW("Failed to create listen socket");

		u_long arg = 1;
//		ioctlsocket(listenSocket,FIONBIO,&arg);

		if (bind( listenSocket, m_pAddressInfo->ai_addr, (int)m_pAddressInfo->ai_addrlen) == SOCKET_ERROR)
			VTHROW("Failed to bind listen socket");

		hListenThread = CreateThread(NULL, 0, ListenThread, (void*)this, CREATE_SUSPENDED, NULL);
		if(hListenThread == NULL)
			VTHROW( "Thread creation failed");

		m_listening = true;
		if(ResumeThread(hListenThread) < 0)
			VTHROW( "Thread execution failed");
	
		m_hListenThread = hListenThread;
		m_listenSocket = listenSocket;
		return true;

	}VCATCHONLY;

	if(hListenThread) {
		TerminateThread(hListenThread,-1);
		CloseHandle(hListenThread);
		hListenThread = NULL;
	}

	m_listening = false;
	m_listenSocket = INVALID_SOCKET;
	hListenThread = NULL;

	return false;
}

bool Server::EndListenForClients()
{
	bool result = true;

	if(!m_listening) {
		return false;
	}

	m_listening = false;
	if(WAIT_OBJECT_0 != WaitForSingleObject(m_hListenThread, 100)) {
	
		TerminateThread(m_hListenThread,-1);
	}
	
	result = result && (CloseHandle(m_hListenThread) == 0);
	result = result && (closesocket(m_listenSocket) != SOCKET_ERROR);

	m_hListenThread = NULL;
	m_listenSocket = INVALID_SOCKET;

	return result;
}

bool Server::SendDataPacket(void* pData, unsigned int sizeInBytes, int packetID, unsigned int clientIdx)
{
	bool result;
	//EnterCriticalSection(&criticalSection);
	if(clientIdx > m_clients.size() - 1)
		result = false;
	else
		result = m_clients[clientIdx]->SendDataPacket(pData, sizeInBytes, packetID);
	//LeaveCriticalSection(&criticalSection);
	return result;
}

bool Server::GetDataPackets(std::vector<Connection::Packet> *pDataPacketList, unsigned int clientID)
{
	if(clientID > m_clients.size() - 1 || !pDataPacketList)
		return false;
	
	bool result;
	//EnterCriticalSection(&criticalSection);
	result = m_clients[clientID]->GetDataPackets(pDataPacketList);
	//LeaveCriticalSection(&criticalSection);
	return result;
}

bool Server::Update() {
	
	bool result = false;
	//EnterCriticalSection(&criticalSection);
	
RESTART_ITERATION:
	for(std::vector<Connection*>::iterator i = m_clients.begin();
		i != m_clients.end(); i++) {
			Connection *pClient = *i;
		//	printf("%i  %i\n", pClient, pClient->m_timeoutTimer );
			if(pClient->m_timeoutTimer > TIMEOUT || !pClient->IsConnected()) {
			
				if(pClient->m_timeoutTimer > TIMEOUT){
			byte b;
			pClient->SendDataPacket(&b, sizeof(byte), NETWORK_DISCONNECT);
				}
			pClient->Release();
			m_clients.erase(i);
			delete pClient;
			pClient = NULL;
			//because the iterator is ruined if we dont reaquire
			goto RESTART_ITERATION;
		} 
		else {

			bool durationEstablished = pClient->m_timeoutTimer > (TIMEOUT / 4);		//Is alive queries can corrupt other data streams that occour over multpile recv() calls so we only want to send them if nothing has happended for a while
			bool durationInterval = pClient->m_timeoutTimer % (TIMEOUT / 4) == 0;	//Dont want to clog the stream with queries either so send them on intervals

			if(durationEstablished && durationInterval) {
				byte b;
				pClient->SendDataPacket(&b, sizeof(byte), NETWORK_PROBE);

			}
		pClient->m_timeoutTimer++;
		}

	}
	//LeaveCriticalSection(&criticalSection);
	return result;
}



bool Server::CheckForClient(unsigned int clientID)
{
	bool result;
	//EnterCriticalSection(&criticalSection);
	result = (m_clients.size() > 0 && clientID < m_clients.size());
	//LeaveCriticalSection(&criticalSection);
	return result;
}

unsigned int Server::ClientCount()
{
	unsigned int count = 0;
	//EnterCriticalSection(&criticalSection);
	count = m_clients.size();
	//LeaveCriticalSection(&criticalSection);
	return count;
}

DWORD WINAPI Server::ListenThread( void* lpParam ) 
{ 	
	Server *pServer = (Server*)lpParam;
	SOCKET ListenSocket = pServer->m_listenSocket;
	
	VTRY {

		if ( listen( ListenSocket, SOMAXCONN ) == SOCKET_ERROR )
			VTHROW("Failed to listen on listen socket");

		while(pServer->m_listening)
		{
			SOCKET clientSocket = accept(ListenSocket, NULL, NULL);
			if (clientSocket == INVALID_SOCKET)
				continue;
			else
			{
				u_long arg = 1;
				//ioctlsocket(clientSocket,FIONBIO,&arg);

				Connection *pNewClient = new Connection(pServer);
				if(pNewClient->Initalize(clientSocket) == false) {
					pNewClient->Release();
					delete pNewClient;
				} 
				else {
/*
					unsigned int newClientIdx = pServer->m_clients.size();
					pNewClient->SendDataPacket((void*)&newClientIdx, sizeof(unsigned int), ID_SETCLIENTINDEX);
					pNewClient->SetServerID((int)newClientIdx);
*/
//				EnterCriticalSection(&pServer->criticalSection);
					pServer->m_clients.push_back(pNewClient);
//					LeaveCriticalSection(&pServer->criticalSection);
				}
			}
		}

		return 0;

	}VCATCHONLY;

	return -1;
}